<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>卡片甲板旋转木马示例</title>
    <style>
      * {
        box-sizing: border-box;
      }

      html {
        font-size: 16px;
      }

      body {
        font-family: sans-serif;
        display: flex;
        justify-content: center;
        align-items: center;
        width: 100vw;
        min-height: 100vh;
        overflow-x: hidden;
      }

      .carousel {
        width: 100vw;
        max-width: 1000px;
        padding: 10px;
      }

      .carousel-content {
        display: flex;
        flex-flow: row nowrap;
        justify-content: center;
        gap: 16px;
      }

      .card {
        display: flex;
        flex-flow: column nowrap;
        justify-content: space-between;
        min-width: 300px;
        padding: 16px;
        background-color: #fff;
        border: 1px solid black;
        border-radius: 10px;
        box-shadow: 2px 4px #0007;
      }

      .card-photo img {
        width: 100%;
        margin-bottom: 1rem;
        pointer-events: none;
      }
    </style>
  </head>
  <body>
    <div class="carousel">
      <div class="carousel-content">
        <div class="card card-photo">
          <img
            src="https://images.unsplash.com/photo-1498673394965-85cb14905c89?crop=entropy&cs=srgb&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3NDU4NTM0OTd8&ixlib=rb-4.0.3&q=85"
            alt="A person's hand holding a sparkler"
            title="Photo by Cristian Escobar for Unsplash"
          />
          <h2>Cool Card Tricks</h2>
          <p>Is this cool? If so, please give a heart!</p>
          <a href="#" class="read-more">Read more</a>
        </div>
        <div class="card card-photo">
          <img
            src="https://images.unsplash.com/photo-1498673394965-85cb14905c89?crop=entropy&cs=srgb&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3NDU4NTM0OTd8&ixlib=rb-4.0.3&q=85"
            alt="A person's hand holding a sparkler"
            title="Photo by Cristian Escobar for Unsplash"
          />
          <h2>Doel Javid</h2>
          <p>He's a programmer, a designer, you name it!</p>
          <a href="#" class="read-more">Read more</a>
        </div>
        <div class="card card-photo">
          <img
            src="https://images.unsplash.com/photo-1498673394965-85cb14905c89?crop=entropy&cs=srgb&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3NDU4NTM0OTd8&ixlib=rb-4.0.3&q=85"
            alt="A person's hand holding a sparkler"
            title="Photo by Cristian Escobar for Unsplash"
          />
          <h2>Drag Me!</h2>
          <p>Drag me to scroll.</p>
          <a href="#" class="read-more">Read more</a>
        </div>
        <div class="card card-photo">
          <img
            src="https://images.unsplash.com/photo-1498673394965-85cb14905c89?crop=entropy&cs=srgb&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3NDU4NTM0OTd8&ixlib=rb-4.0.3&q=85"
            alt="A person's hand holding a sparkler"
            title="Photo by Cristian Escobar for Unsplash"
          />
          <h2>Infinite Scrolling</h2>
          <p>You can scroll on, and on, and on...</p>
          <a href="#" class="read-more">Read more</a>
        </div>
        <div class="card card-photo">
          <img
            src="https://images.unsplash.com/photo-1498673394965-85cb14905c89?crop=entropy&cs=srgb&fm=jpg&ixid=M3wzMjM4NDZ8MHwxfHJhbmRvbXx8fHx8fHx8fDE3NDU4NTM0OTd8&ixlib=rb-4.0.3&q=85"
            alt="A person's hand holding a sparkler"
            title="Photo by Cristian Escobar for Unsplash"
          />
          <h2>Never-ending Scroll</h2>
          <p>The scrolling never ends!</p>
          <a href="#" class="read-more">Read more</a>
        </div>
      </div>
    </div>
  </body>

  <script src="https://cdn.jsdelivr.net/npm/animejs/lib/anime.iife.min.js"></script>
  <script>
    // 从anime.js库中导入需要的函数
    const { animate, createDraggable, utils } = anime

    // 等待DOM加载完成后执行初始化
    document.addEventListener('DOMContentLoaded', () => {
      // 获取轮播容器元素
      const carousel = document.querySelector('.carousel')
      // 获取轮播内容容器
      const carouselContent = carousel.querySelector('.carousel-content')
      // 获取所有卡片元素
      const carouselCards = carouselContent.querySelectorAll('.card')

      // 根据卡片位置更新所有卡片的transform样式
      // 计算每个卡片的旋转角度、位移和透明度，实现3D效果
      // 定义更新卡片位置的函数
      const updateCards = () => {
        // 获取轮播容器的边界矩形
        const carouselRect = carousel.getBoundingClientRect()

        // 遍历所有卡片，更新每个卡片的位置
        carouselContent.querySelectorAll('.card').forEach((card) => {
          // 获取当前卡片的边界矩形
          const { left, right } = card.getBoundingClientRect()
          // 计算卡片中心点位置
          const center = (left + right) / 2
          // 计算卡片相对于轮播容器的中心偏移量
          const posFromCenter = (carouselRect.left + carouselRect.right) / 2 - center
          // 计算卡片到中心的绝对距离
          const distFromCenter = Math.abs(posFromCenter)

          // 根据偏移量设置卡片的旋转角度和位移，实现3D效果
          card.style['transform'] = `rotate(${posFromCenter / -30}deg)
			translate(
				${Math.sin((posFromCenter / carouselRect.width) * Math.PI) * 30}px,
				${(Math.cos(((distFromCenter / carouselRect.width) * Math.PI) / 1) - 1) * -105}px
			)`
          // 根据偏移量设置卡片的z-index，实现层叠效果
          card.style['z-index'] = Math.round(
            Math.cos((posFromCenter / carouselRect.width) * Math.PI) * 5,
          )
          // 根据距离设置卡片的透明度，实现淡出效果
          card.style['opacity'] = `${Math.min(
            ((carouselRect.width - distFromCenter) / carouselRect.width) * 1.675,
            1,
          )}`
        })
      }

      // 创建可拖拽实例，实现卡片的拖拽功能
      const draggable = createDraggable(carouselContent, {
        // 配置水平方向的拖拽参数
        // 配置水平方向的动画参数
        x: {
          // 设置拖拽的吸附距离
          snap: 316,
          // 定义拖拽值的修改器
          modifier: (value) => {
            // 计算轮播容器的总宽度
            const carouselSize = carouselCards.length * 316
            // 使用wrap函数实现无限循环效果
            return utils.wrap(value, -carouselSize / 2 - 1, carouselSize / 2 + 1)
          },
        },
        // 禁用垂直方向的拖拽
        y: false,
        // 设置拖拽速度的乘数
        velocityMultiplier: 3,
        // 拖拽时调用更新卡片位置的函数
        // 动画更新时调用更新卡片位置的函数
        onUpdate: updateCards,
        // 窗口大小改变时调用更新卡片位置的函数
        onResize: updateCards,
      })

      // 创建动画实例，实现初始化动画
      animate(carouselContent, {
        // 配置水平方向的拖拽参数
        // 配置水平方向的动画参数
        x: {
          // 设置动画的起始位置
          from: 4000,
          // 设置动画的结束位置
          to: 0,
          // 设置动画的缓动函数
          ease: 'out(4)',
        },
        // 设置动画持续时间
        duration: 2400,
        // 拖拽时调用更新卡片位置的函数
        // 动画更新时调用更新卡片位置的函数
        onUpdate: updateCards,
      })

      {
        let lastCard = null
        carouselCards.forEach((card) => {
          const duplicateCard = card.cloneNode(true)
          duplicateCard.ariaHidden = true

          if (lastCard) {
            lastCard.after(duplicateCard)
            lastCard = duplicateCard
          } else {
            carouselContent.prepend(duplicateCard)
          }

          lastCard = duplicateCard
        })
      }
      carouselCards.forEach((card) => {
        const duplicateCard = card.cloneNode(true)
        duplicateCard.ariaHidden = true
        carouselContent.append(duplicateCard)
      })
      updateCards()
    })
  </script>
</html>
